MODULE = PlPN          PACKAGE = PlPN::Document

#
# this package doesn't used the standard XS/C++ interface
# becaause the object is a boost shared_ptr, not a real pointer
#

#
# Pod::Html is kind of brain-dead:
#
# Place Z<> between function names and parentheses, otherwise Pod::Html
# assumes you meant to put C<...> around it
#
# Put explicit text in all links L<use my text, stupid|some::perl::module>,
# otherwise Pod::Html assumes you want "the X manpage". (Does not apply to
# relative links withing this file.)
#
# It also assumes you want a link if you have C<...> with text matching a
# potential link target. The only way I've found around that is to split it
# up into multiple pieces: C<wo>C<rd>. Looks correct in the resulting HTML,
# but it's ugly to have <code>wo</code><code>rd</code> in the source, and I
# shouldn't have to do it.
#

=head1 NAME

PlPN::Document - The document object

=head1 SYNOPSIS

	use PlPN qw(PN);
	
	my $doc = PN->GetCurrentDocument;
	my $title = $doc->GetTitle;
	
	# etc.

=head1 DESCRIPTION

Allows manipulation of Programmer's Notepad documents from Perl.

=head1 METHODS

=over

=cut

void
DESTROY(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		TrackedDocument* tdoc = g_document_tracker.Fetch(THIS);
		if (tdoc != NULL) {
			tdoc->document_sink->DetachAllPerlSinks();
			tdoc->text_editor_sink->DetachAllPerlSinks();
		}

		// delete our pointer to a shared_ptr
		// so our copy of the shared_ptr can go out of scope
		delete THIS_ptr;	 // THIS_ptr created by typemap

FALLBACK: TRUE

bool
eq(a,b,swap)
		extensions::IDocumentPtr a;
		extensions::IDocumentPtr b;
		IV swap;
	OVERLOAD: ==
	CODE:
		RETVAL = (a == b);
	OUTPUT:
		RETVAL

#
# xsubpp only generates the following code for the module, not for the
# individual packages. In our case, that's less than useful, because the
# module does not, in fact, have overloaded operators.
# See the boot section in the generated code for an explanation of what the
# following code does
#
BOOT:
PL_amagic_generation++;
sv_setsv(
	get_sv( "PlPN::Document::()", TRUE ),
	&PL_sv_yes	// or &PL_sv_no or &PL_sv_undef
);
(void)newXSproto_portable("PlPN::Document::()", XS_PlPN_nil, file, "$$$");

=item GetTitleZ<>()

Returns the title of the document (the text that appears in the tab).

=cut

const wchar_t*
GetTitle(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->GetTitle();
	OUTPUT:
		RETVAL

=item GetFileNameZ<>()

Returns the file name associated with the document.

=cut

const wchar_t*
GetFileName(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->GetFileName();
	OUTPUT:
		RETVAL

=item GetCurrentSchemeZ<>()

Returns the scheme associated with the document.

=cut

const char*
GetCurrentScheme(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->GetCurrentScheme();
	OUTPUT:
		RETVAL

=item GetScintillaHWNDZ<>()

Returns the handle of the Scintilla editor associated with the object.
Probably not very useful unless you're using a GUI package
(such as L<Win32::GUI|Win32::GUI>).

=cut

HWND
GetScintillaHWND(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->GetScintillaHWND();
	OUTPUT:
		RETVAL

=item GetModifiedZ<>()

Returns true if the document has been modified since the last time it was
saved; false otherwise.

More precisely, the reference action is the actual saving of the file to
disk, but the setting of a save point in the Scintilla object. However,
unless someone has been messing with the Scintilla object directly, the two
should happen at the same time.

The extensions interface, does however, permit messing with the Scintilla
object (see L</GetScintillaHWND> and L</SendEditorMessage>), so it is
possible for an extension (including this one) to set a save point without
actually saving the file.

=cut

bool
GetModified(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->GetModified();
	OUTPUT:
		RETVAL

=item GetWriteProtectZ<>()

Returns true if the document if write-protected (i.e., read-only); false
otherwise.

Note that this refers only to the status of the document object, and not to
the status of the associated file (although the two are usually connected).

=cut

bool
GetWriteProtect(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->GetWriteProtect();
	OUTPUT:
		RETVAL

=item GetCanSaveZ<>()

Returns true if the document can be saved. For Programmer's Notepad, it can
be saved if it is associated with a valid filename.

=cut

bool
GetCanSave(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->GetCanSave();
	OUTPUT:
		RETVAL

=item SaveZ<>($filename, $update = 1)

Saves the file. If C<$update> is true, it will update the UI; this includes
changing the tab text if necessary, setting a save point, and sending
C<OnBeforeSave> and C<OnAfterSave> events. Setting C<$update> to a false
value basically means you're saving a copy that the UI doesn't need to know
about, perhaps a timed backups.

Returns true on success, false on failure.

=cut

bool
Save(THIS, filename, setFilename = true)
		extensions::IDocumentPtr THIS;
		const wchar_t* filename;
		bool setFilename;
	CODE:
		RETVAL = THIS->Save(filename, setFilename);
	OUTPUT:
		RETVAL
	CLEANUP:
		// caller responsible for freeing memory after utf8_2_wide
		free((void*)filename);

=item SendEditorMessageZ<>($message, $wParam, $lParam)

Sends a message to the associated Scintilla window.

See L<http://www.scintilla.org/ScintillaDoc.html> for information on
Scintilla messages and associated parameters. See
L<http://scintilla.hg.sourceforge.net/hgweb/scintilla/scintilla/file/tip/include/Scintilla.h>
for the values of the Scintilla message constants.

=cut

LRESULT
SendEditorMessage(THIS, msg, wParam = 0, svParam = &PL_sv_undef)
		extensions::IDocumentPtr THIS;
		UINT msg;
		WPARAM wParam;
		SV* svParam;
	CODE:
		if (SvPOK(svParam)) {
			// operate directly on the SV*, not on a copy
			// in case the message changes what's at the pointer
			sv_catpvn(svParam, "\0", 1);	// make sure we're null-terminated
			const char* strParam = SvPV_nolen(svParam);
			RETVAL = THIS->SendEditorMessage(msg, wParam, strParam);
		}
		else {	// assume integer
			LPARAM lParam  = (LPARAM)SvIV(svParam);
			RETVAL = THIS->SendEditorMessage(msg, wParam, lParam);
		}
	OUTPUT:
		RETVAL

=item IsValidZ<>()

Returns true if the document object is valid, false otherwise. A document
object is invalid if the underlying document has been closed.

=cut

bool
IsValid(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		RETVAL = THIS->IsValid();
	OUTPUT:
		RETVAL
		
=item AddEventSinkZ<>($sink)

=item RemoveEventSinkZ<>($sink)

Adds or removes a L<PlPN::DocumentEventSink|PlPN::DocumentEventSink> or
L<PlPN::TextEditorEventSink|PlPN::TextEditorEventSink>. You only need this
if you're writing your own event sink, you don't need it if you're using the
default event sink via L<PlPN::RegisterEvent|PlPN/RegisterEvent>.

=cut

void
AddEventSink(THIS, sinksv)
		extensions::IDocumentPtr THIS;
		SV* sinksv;
	CODE:
		TrackedDocument* tdoc = g_document_tracker.Fetch(THIS);
		if (sv_derived_from(sinksv, "PlPN::DocumentEventSink")) {
			tdoc->document_sink->AttachPerlSink(sinksv);
		}
		else if (sv_derived_from(sinksv, "PlPN::TextEditorEventSink")) {
			tdoc->text_editor_sink->AttachPerlSink(sinksv);
		}
		else {
			croak_xs_usage(cv,  "THIS, sink (must be DocumentEventSink or TextEditorEventSink)");
		}

void
RemoveEventSink(THIS, sinksv)
		extensions::IDocumentPtr THIS;
		SV* sinksv;
	CODE:
		TrackedDocument* tdoc = g_document_tracker.Fetch(THIS);
		if (sv_derived_from(sinksv, "PlPN::DocumentEventSink")) {
			tdoc->document_sink->DetachPerlSink(sinksv);
		}
		else if (sv_derived_from(sinksv, "PlPN::TextEditorEventSink")) {
			tdoc->text_editor_sink->DetachPerlSink(sinksv);
		}
		else {
			croak_xs_usage(cv,  "THIS, sink (must be DocumentEventSink or TextEditorEventSink)");
		}

=item FindNextZ<>($options)

Performs a search using the values from C<$options>, a
L<PlPN::SearchOptions|PlPN::SearchOptions> object. It returns one of the
indicators exposed via the C<:FindNextResult> tag in L<PlPN|PlPN>. These
indicators are:

=over

=item fnNotFound

=item fnFound

=item fnReachedStart

=item fnInvalidRegex

=item fnInvalidSearch

=back

=cut

FindNextResult
FindNext(THIS, options)
		extensions::IDocumentPtr THIS;
		extensions::ISearchOptions* options;
		const char* CLASS = "PlPN::FindNextResult";
	CODE:
		RETVAL = THIS->FindNext(options);
	OUTPUT:
		RETVAL

=item ReplaceZ<>($options)

Performs a search and replace using the values from C<$options>, a
L<PlPN::SearchOptions|PlPN::SearchOptions> object. It returns true if a
match was found and replaced; false otherwise.

=cut

bool
Replace(THIS, options)
		extensions::IDocumentPtr THIS;
		extensions::ISearchOptions* options;
	CODE:
		RETVAL = THIS->Replace(options);
	OUTPUT:
		RETVAL
		
=item ReplaceAllZ<>($options)

Performs a search and replace all using the values from C<$options>, a
L<PlPN::SearchOptions|PlPN::SearchOptions> object. It returns the number of
matches found and replaced.

=cut

int
ReplaceAll(THIS, options)
		extensions::IDocumentPtr THIS;
		extensions::ISearchOptions* options;
	CODE:
		RETVAL = THIS->ReplaceAll(options);
	OUTPUT:
		RETVAL
		
=item CloseZ<>($force = 0)

Closes the document. If the document has not been changed since the last
save point, it will be closed. If the document has been changed, however,
the default behavior is to notify the user, who can then choose whether to
close the file, save and close the file, or keep the file open. Setting
C<$force> to a true value will override this default behavior and close the
file without prompting the user.

=cut

void
Close(THIS, dontAskUserIfUnsaved = false)
		extensions::IDocumentPtr THIS;
		bool dontAskUserIfUnsaved;
	CODE:
		THIS->Close(dontAskUserIfUnsaved);
		
=item ActivateZ<>()

Activates the document, including bringing the document window to the front
of other document windows and shifting focus to it.

=cut

void
Activate(THIS)
		extensions::IDocumentPtr THIS;
	CODE:
		THIS->Activate();

=back

=head1 COPYRIGHT and LICENCE

Copyright (c) 2012 Sean Healy. All rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut
